`timescale 1ns/1ns
//新建的针对AE350 的DDR3 IP, 256b DQ,32b mask

module m_ahb_ddr3
(
    // DDR3 I/F
    input   wire            DDR3_MEMORY_CLK,    // 不需要
    input   wire            DDR3_CLK_IN,        // 不需要,提供给ddr IP使用50Mhz
    input   wire            DDR3_RSTN,          // Reset
    input   wire            DDR3_LOCK,          // 不需要
    output  wire            DDR3_STOP,          // 不需要
    input 	wire            DDR3_INIT,          // ddr初始化完成

    //下列由SOC提供
    input   wire            HCLK,               //由SOC.DDR_HCLK
    input   wire            HRESETN,            //SOC.DDR_HRSTN
    input   wire    [31:0]  HADDR,
    input   wire    [2:0]   HSIZE,
    input   wire            HWRITE,
    input   wire    [1:0]   HTRANS,
    input   wire    [2:0]   HBURST,
    input   wire    [63:0]  HWDATA,
    output  reg             HREADY_O,
    output  wire    [1:0]   HRESP,
    output  reg     [63:0]  HRDATA,
    //

    //外部调用memory IP
    // DDR Signals
    input wire        ddr_clk,
    output reg        [31 : 0]      cmd_addr,
    output reg        [128-1 : 0]   wr_data,
    input wire        [128-1 : 0]   lpddr_rd_data,
    input wire        lpddr_rd_valid,
    input wire        lpddr_rd_end,
    output reg        [16-1 : 0]    lpddr_wdata_mask,
    output reg        lpddr_cmd_en,
    output reg        lpddr_cmd,
    input wire        lpddr_cmd_ready,
    input wire        lpddr_data_ready,
    output reg        lpddr_wdata_en,
    output reg        lpddr_wdata_end
);


    assign HRESP = 2'b00;
    /////////////////////////////////////////////////////////////////////////////////////////////
    //SOC控制
    wire hsel = HTRANS[1];
    reg [3:0]   c_state;
    reg [31:0]  wr_addr;
    reg [7:0]   wr_data_mask;
    wire [15:0]  wr_data_mask_shift =
            (wr_addr[2:0]=='d0)? {8'd0,wr_data_mask}:
            (wr_addr[2:0]=='d1)? {7'd0,wr_data_mask,1'd0}:
            (wr_addr[2:0]=='d2)? {6'd0,wr_data_mask,2'd0}:
            (wr_addr[2:0]=='d3)? {5'd0,wr_data_mask,3'd0}:
            (wr_addr[2:0]=='d4)? {4'd0,wr_data_mask,4'd0}:
            (wr_addr[2:0]=='d5)? {3'd0,wr_data_mask,5'd0}:
            (wr_addr[2:0]=='d6)? {2'd0,wr_data_mask,6'd0}:
            (wr_addr[2:0]=='d7)? {1'd0,wr_data_mask,7'd0}:0;


    localparam [4: 0]  C_IDLE               = 4'd0;
    localparam [4: 0]  C_WRITE              = 4'd1;
    localparam [4: 0]  C_READ               = 4'd2;

    reg [64-1:0] hwdata;
    wire [32+16+128+1+1-1     :0] fifo_cmd_data;
    reg     cmd_en;
    assign      fifo_cmd_data = {wr_addr,~wr_data_mask_shift,64'd0,HWDATA,c_state==C_WRITE,cmd_en};
    reg         fifo_rd_empty_nt;
    reg         fifo_cmd_wr = 0;
    reg         cmd_en_nt;

    always @(posedge HCLK or negedge HRESETN) begin

        if(!HRESETN)begin
            c_state          <= C_IDLE;
            HREADY_O         <= 1'b1;
            cmd_en           <= 0;
            fifo_cmd_wr      <= 0;
            wr_data_mask     <= 0;
            hwdata           <= 0;
            fifo_rd_empty_nt <= 0;
            cmd_en_nt        <= 0;
        end
        else begin
            fifo_rd_empty_nt <= fifo_rd_empty;
            case (c_state)
                C_WRITE:begin
                    cmd_en          <= cmd_en_nt;
                    fifo_cmd_wr     <= cmd_en|cmd_en_nt;      //需要压第二个数据,所有为空
                    cmd_en_nt       <= 0;

                    //当前状态为写操作,需要判断是否写入结束.切换状态
                    //在fifo_rd中读取状态
                    //if(wr_data_end)begin
                    if(!fifo_rd_empty_nt)begin
                        //暂时替代
                        c_state     <= C_IDLE;
                        HREADY_O    <= 1'b1;
                    end
                    //end
                end
                C_READ:begin
                    fifo_cmd_wr <= cmd_en; //需要压第二个数据,所有为空
                    cmd_en      <= 1'b0;
                    if(!fifo_rd_empty_nt)begin
                        //读取数据
                        c_state     <= C_IDLE;
                        HRDATA      <= lpddr_rdata_buf[63:0];
                        HREADY_O    <= 1'b1;
                    end
                end
                default:begin
                    //当前状态为空闲
                    //判断状态
                    case (1'b1)
                        HTRANS[1]&&HWRITE:begin
                            //写操作,为下一个周期设置读写操作
                            c_state      <= C_WRITE;
                            wr_data_mask <= (HSIZE==3'b000)?8'b0000_0001:
                                            (HSIZE==3'b001)?8'b0000_0011:
                                            (HSIZE==3'b010)?8'b0000_1111:
                                            (HSIZE==3'b011)?8'b1111_1111:8'd0;
                            hwdata       <= HWDATA;
                            wr_addr      <= HADDR;
                            HREADY_O     <= 1'b0;
                            cmd_en_nt    <= 1'b1;
                            fifo_cmd_wr  <= 1'b1;
                        end
                        HTRANS[1]&&!HWRITE:begin
                            //读操作,直接设置addr读取
                            c_state      <= C_READ;
                            wr_addr      <= HADDR;
                            HREADY_O     <= 1'b0;
                            cmd_en        <= 1'b1;
                            fifo_cmd_wr  <= 1'b1;
                        end
                        default:begin
                            cmd_en      <= 1'b0;
                            fifo_cmd_wr <= 1'b0;
                        end
                    endcase
                end

            endcase
        end
    end
    ////////////////////////////
    //ddr3 cmd
    wire fifo_cmd_empty,fifo_cmd_full;
    wire [32+16+128+1+1-1     :0] fifo_cmd_data_o;
    wire [32-1:0]           fifo_cmd_addr   ;
    wire [16-1:0]           fifo_cmd_wr_mask;
    wire [128-1:0]          fifo_cmd_wr_data;
    wire fifo_cmd_write,fifo_cmd_en;        //1+1
    assign {fifo_cmd_addr,fifo_cmd_wr_mask,fifo_cmd_wr_data,fifo_cmd_write,fifo_cmd_en} = fifo_cmd_data_o;

    //ddr3_memory_cmd_fifo u_fifo_cmd
`ifdef SIM
    fifo #(
        .WIDTH(32+16+128+1+1),
        .DEPTH(64)
    )
`else
    ddr3_memory_cmd_fifo
`endif
    u_fifo_cmd
    (
        .Reset        (!HRESETN),

        .WrClk        (HCLK),
        .WrEn         (fifo_cmd_wr      ),
        .Data         (fifo_cmd_data    ),

        .RdClk        (ddr_clk          ),
        //.RdEn         (1'b1             ),          //实际上可以一直为1,默认是读取值全为0
        .RdEn         (lpddr_cmd_ready ),          //需要空闲才能读取
        .Q            (fifo_cmd_data_o  ),

        .Empty        (fifo_cmd_empty),
        .Full         (fifo_cmd_full),
        .Almost_Empty (),
        .Almost_Full  ()
    );

    //////////////////////////////////////////////////////////////////////////////////////////////
    //ddr端操作
    wire fifo_rd_empty;
    wire fifo_rd_full;
    reg[128-1:0]    fifo_rd_data;
    wire[128-1:0]   lpddr_rdata_buf;
    reg          fifo_rd_wr;

`ifdef SIM
    fifo #(
        .WIDTH(128),
        .DEPTH(64)
    )
`else
    ddr3_memory_rdfifo
`endif
    u_fifo_rd
    (
        .Reset        (!HRESETN),

        .WrClk        (ddr_clk),
        .WrEn         (fifo_rd_wr),
        .Data         (fifo_rd_data),

        .RdClk        (HCLK),
        .RdEn         (1'b1),                           //可以一直读取,如果是!fifo_rd_empty,则下一个时钟可以读取到数据
        .Q            (lpddr_rdata_buf),

        .Empty        (fifo_rd_empty),
        .Full         (fifo_rd_full),
        .Almost_Empty (),
        .Almost_Full  ()
    );

    reg         lpddr_read;
    reg         wait_write;
    //读取数据处理
    always @(posedge ddr_clk) begin
        if(!HRESETN)begin
            fifo_rd_wr       <= 0;
        end
        else if(fifo_rd_wr)begin
            fifo_rd_wr      <= 0;
        end
        else if(lpddr_wdata_en)begin
            fifo_rd_wr      <= 1'b1;
        end
        else if(lpddr_read && lpddr_rd_valid && lpddr_rd_end)begin
            fifo_rd_data    <= lpddr_rd_data;// << {cmd_addr[2:0],3'd0};
            fifo_rd_wr      <= 1'b1;
        end
    end

    always @(posedge ddr_clk) begin
        if(!HRESETN)begin
            lpddr_cmd_en <= 0;
            lpddr_cmd    <= 0;
            lpddr_wdata_mask <= 16'd0;
            cmd_addr         <= 32'd0;
            wr_data          <= 128'd0;
            lpddr_wdata_en   <= 1'd0;
            lpddr_wdata_end  <= 1'd0;
            lpddr_read       <= 0;
            wait_write       <= 0;
        end
        else begin
            if(wait_write)begin
                //等待可写
                if(lpddr_data_ready)begin
                    wait_write      <= 0;
                    lpddr_cmd       <= 0;       //0为可写
                    lpddr_cmd_en    <= 1;
                    lpddr_wdata_en  <= 1;
                    lpddr_wdata_end <= 1;
                end
            end
            else if(lpddr_read)begin
                //现在处于正在读取数据状态
                if(lpddr_rd_valid && lpddr_rd_end)begin
                    lpddr_read      <= 1'b0;
                end
                lpddr_cmd <= 0;
                lpddr_cmd_en <= 0;
            end
            else if(lpddr_wdata_en) begin
                //上一个状态写入数据,清空写入
                lpddr_wdata_en      <= 1'b0;
                lpddr_wdata_end     <= 1'b0;
                lpddr_cmd           <= 1'b0;
                lpddr_cmd_en        <= 1'b0;
                wr_data             <= 0;
                lpddr_wdata_mask    <= 0;
            end
            else if(fifo_cmd_en)begin
                //fifo中存在命令,执行
                if(lpddr_cmd_ready)begin
                    //ddr状态处于空闲中

                    lpddr_wdata_en      <= fifo_cmd_write & lpddr_data_ready;
                    lpddr_wdata_end     <= fifo_cmd_write & lpddr_data_ready;   //只写入一个数据?

                    //数据与mask
                    wr_data             <= fifo_cmd_wr_data;
                    lpddr_wdata_mask    <= fifo_cmd_wr_mask;

                    if(lpddr_data_ready)begin
                        //需要ddr处于可写状态
                        //有时候,这个处于低状态,那么需要等待处理
                    end
                    else begin
                        //需要等待写入
                        wait_write          <= fifo_cmd_write;
                    end

                    lpddr_cmd           <= ~(fifo_cmd_write & lpddr_data_ready);                        //0为写,1为读. 如果读状态,cmd会处于1
                    lpddr_cmd_en        <= ~fifo_cmd_write || (lpddr_data_ready & fifo_cmd_write);      //

                    lpddr_read          <= ~fifo_cmd_write;  //读取数据状态
                end

            end
            cmd_addr <= fifo_cmd_addr;
        end
    end

    ///////////////////////////////////////////////////////////////////////////////////////////////
endmodule


`ifdef SIM
module sim_tb ();
    //iverilog -DSIM -f sim.txt && vvp a.out
    reg clk = 0;
    reg rst_n = 0;
    reg memory_clk = 0;
    wire clk50m = clk;
    wire DDR_HCLK = clk50m;

    always clk = #10 ~clk;
    always memory_clk = #1.25 ~memory_clk;
    always rst_n = #1000 1;
    always #50000 $finish;


    wire [63:0] DDR_HRDATA;
    wire DDR_HREADY;
    wire [1:0]  DDR_HRESP;
    wire [2:0]  DDR_HBURST;
    wire [3:0]  DDR_HPROT;




    wire DDR_HRSTN = 1;
    reg  DDR_HWRITE = 0;
    reg  [31:0]     DDR_HADDR = 0;
    reg  [1:0]      DDR_HTRANS = 0;
    reg  [2:0]      DDR_HSIZE = 0;
    reg  [63:0]     DDR_HWDATA;
    reg             test = 1;
    always #20000 test = 0;
    reg [31:0]  counter = 0;

    always @(posedge clk50m)begin
        counter     <=  counter + 1;
        if(!test)begin
            test <= 1;
            DDR_HTRANS <= 2'b10;
            DDR_HSIZE  <= 3'b010;
            DDR_HADDR  <= 32'h12345678+7;
            DDR_HWRITE <= 1;
            counter    <= 0;
        end
        else if(DDR_HTRANS!=0)begin
            DDR_HWDATA <= 32'h12345678;
            DDR_HTRANS <= 0;
        end
        else begin
            DDR_HTRANS <= 0;
            DDR_HSIZE  <= 0;
        end

    end


    m_ahb_ddr3 u_gw_ahb_ddr3_top
    (
        // DDR3 I/F
        .DDR3_MEMORY_CLK        (memory_clk),   // Memory clock 400MHz
        .DDR3_CLK_IN            (clk50m),       // Clock input 50MHz
        .DDR3_RSTN(rst_n),                      // Reset input
        .DDR3_LOCK(1'b1),                       // PLL lock
        //.DDR3_STOP(1'b1),                       // PLL stop
        .DDR3_INIT(1'b1),                       // Initialized

        .HCLK(DDR_HCLK),
        .HRESETN(rst_n),
        .HADDR(DDR_HADDR),
        .HSIZE(DDR_HSIZE),
        .HWRITE(DDR_HWRITE),
        .HTRANS(DDR_HTRANS),
        .HBURST(DDR_HBURST),
        .HWDATA(DDR_HWDATA),
        .HREADY_O(DDR_HREADY),
        .HRESP(DDR_HRESP),
        .HRDATA(DDR_HRDATA),

        //AHB总线操作memory
        .ddr_clk            (clk50m          ) ,
        .cmd_addr           (        ),
        .wr_data            (         ),
        .lpddr_rd_data      (128'd0  ),
        .lpddr_rd_valid     (counter>20),
        .lpddr_rd_end       (counter>20),
        .lpddr_wdata_mask   (),
        .lpddr_cmd_en       (lpddr_cmd_en    ),
        .lpddr_cmd          (lpddr_cmd       ),
        .lpddr_cmd_ready    (counter>=0)    ,
        .lpddr_data_ready   (counter>=0)          ,
        .lpddr_wdata_en     (lpddr_wdata_en  ),
        .lpddr_wdata_end    (lpddr_wdata_end )
    );

    initial begin
        $dumpfile("test.vcd");
        $dumpvars(0, sim_tb);
    end
endmodule
`endif
